#ifndef __LOG_HPP__
#define __LOG_HPP__
#include <iostream>
#include <string>
#include <fstream>
#include <filesystem>
#include <unistd.h>
#include <sstream>
#include <ctime>
#include "Mutex.hpp"



// 策略模式，创造一个纯虚的 日志策略 基类
namespace wxd
{
    std::string gsep = "\r\n";
    class LogStrategy
    {
    public:
        // 提供纯虚方法
        virtual void SyncLog(const std::string &msg) = 0;
    };

    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        virtual void SyncLog(const std::string &msg) override
        {
            {
                LockGuard lock(&_mutex);
                // 向console打印
                std::cout << msg << gsep;
            }
        }

    private:
        Mutex _mutex;
    };

    const std::string default_path = "./log";
    const std::string default_file = "my.log";
    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string &path = default_path, const std::string &file = default_file)
            : _path(path), _file(file)
        {
        }
        virtual void SyncLog(const std::string &msg) override
        {
            {
                LockGuard lock(&_mutex);
                // 判断路径是否存在
                if (!std::filesystem::exists(_path))
                {
                    try
                    {
                        std::filesystem::create_directories(_path);
                    }
                    catch (const std::filesystem::filesystem_error &e)
                    {
                        std::cerr << e.what() << '\n';
                    }
                }
                std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;

                // 打开文件
                std::ofstream out(filename, std::ios::app);
                if (!out.is_open())
                {
                    return;
                }
                out << msg << gsep;
                out.close();
            }
        }

    private:
        Mutex _mutex;
        std::string _path; // 文件路径
        std::string _file; // 文件名本身
    };

    // 日志等级
    enum class LogLevel
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    std::string LevelToString(LogLevel level)
    {
        switch (level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
            break;
        case LogLevel::INFO:
            return "INFO";
            break;
        case LogLevel::WARNING:
            return "WARNING";
            break;
        case LogLevel::ERROR:
            return "ERROR";
            break;
        case LogLevel::FATAL:
            return "FATAL";
            break;
        default:
            return "UNKNOWN";
            break;
        }
    }

    std::string GetTime()
    {
        time_t timestamp = time(nullptr);
        struct tm tm_ret;
        localtime_r(&timestamp,&tm_ret);
        char timebuffer[128];

        snprintf(timebuffer,sizeof(timebuffer),"%04d-%02d-%02d %02d:%02d:%02d",tm_ret.tm_year+1900,tm_ret.tm_mon+1,tm_ret.tm_mday,tm_ret.tm_hour,tm_ret.tm_min,tm_ret.tm_sec);
        return timebuffer;
    }


    //暂时声明一下
    class Log;
    class Log
    {
    public:
        Log()
        {
            //默认刷新console
            EnableConsoleLogStrategy();
        }

       
        void EnableConsoleLogStrategy()
        {
            PtrLogStrategy = std::make_unique<ConsoleLogStrategy>();
        }
        void EnableFileLogStrategy()
        {
            PtrLogStrategy = std::make_unique<FileLogStrategy>();

        }

        void fflush(const std::string& msg)
        {
            PtrLogStrategy->SyncLog(msg);
        }

         // 把一整条信息写入到一个内部类
         class LogMessage
         {
         public:
             LogMessage(LogLevel level, const std::string &file, int line,Log& log)
                 : _level(level), _file(file), _line(line), _pid(getpid()), _time(GetTime()),_log(log)
             {
                 // 用C++的接口拼接字符串
                 std::stringstream str;
                 str << "[" << _time << "] "
                     << "[" << LevelToString(_level) << "] "
                     << "[" << _pid << "] "
                     << "[" << _file << "] "
                     << "[" << _line << "] " << "- ";
                 _info = str.str();
             }
 
             template <class T>
             LogMessage&operator<<(const T& r_info)
             {
                 std::stringstream str;
                 str << r_info;
                 _info  += str.str();
                 return *this;
             }


 
             ~LogMessage()
             {
                if(_log.PtrLogStrategy)
                {
                    _log.fflush(_info);
                }
                
             }
             
 
         private:
             LogLevel _level;
             std::string _time;
             pid_t _pid;
             std::string _file;
             int _line;
             std::string _info;
             wxd::Log& _log;
         };


         //这里是利用了C++17一定会对返回值优化的特性
         //经过测试，如果使用C++11/14编译，并不启用g++优化，会调用两次析构函数
         LogMessage operator()(LogLevel level,const std::string &file,int line)
         {
            return LogMessage(level,file,line,*this);
         }



    private:
        std::unique_ptr<LogStrategy> PtrLogStrategy;
    };

    Log logger;
    #define LOG(level) (logger(level,__FILE__,__LINE__))
    #define EnableConsoleLogStrategy() logger.EnableConsoleLogStrategy()
    #define EnableFileLogStrategy() logger.EnableFileLogStrategy()

}

#endif